package com.atguigu.gmall.list.service.impl;

import com.alibaba.fastjson.JSON;
import com.atguigu.gmall.common.result.Result;
import com.atguigu.gmall.list.repository.GoodsRepository;
import com.atguigu.gmall.list.service.SearchService;
import com.atguigu.gmall.model.list.*;
import com.atguigu.gmall.model.product.BaseAttrInfo;
import com.atguigu.gmall.model.product.BaseCategoryView;
import com.atguigu.gmall.model.product.BaseTrademark;
import com.atguigu.gmall.model.product.SkuInfo;
import com.atguigu.gmall.product.client.ProductFeignClient;
import org.apache.lucene.search.TotalHits;
import org.apache.lucene.search.join.ScoreMode;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchRequestBuilder;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.Operator;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.bucket.nested.ParsedNested;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedLongTerms;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedStringTerms;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedTerms;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;

@Service
public class SearchServiceImpl implements SearchService {

    @Autowired
    private GoodsRepository goodsRepository;

    @Autowired
    private ProductFeignClient productFeignClient;

    // 注入redis
    @Autowired
    private RedisTemplate redisTemplate;

    // 注入高级客户端
    @Autowired
    private RestHighLevelClient restHighLevelClient;

    /**
     * 商品的上架功能
     * @param skuId
     */
    @Override
    public void upperGoods(Long skuId) {
        // 封装数据
        Goods goods = new Goods();

        // 使用远程调用获取数据
        // 查询skuinfo
        SkuInfo skuInfo = productFeignClient.getAttrValueList(skuId);
        if (skuInfo != null){
            goods.setId(skuInfo.getId());
            goods.setPrice(skuInfo.getPrice().doubleValue());
            goods.setTitle(skuInfo.getSkuName());
            goods.setDefaultImg(skuInfo.getSkuDefaultImg());
            goods.setCreateTime(skuInfo.getCreateTime());
        }

        // 查询品牌信息，根据品牌id
        BaseTrademark baseTradeMark = productFeignClient.getBaseTradeMark(skuInfo.getTmId());
        if (baseTradeMark != null){
            goods.setTmId(baseTradeMark.getId());
            goods.setTmLogoUrl(baseTradeMark.getLogoUrl());
            goods.setTmName(baseTradeMark.getTmName());
        }

        // 查询分类属性及值
        BaseCategoryView categoryView = productFeignClient.getCategoryView(skuInfo.getCategory3Id());
        if (categoryView!= null){
            goods.setCategory1Id(categoryView.getCategory1Id());
            goods.setCategory2Id(categoryView.getCategory2Id());
            goods.setCategory3Id(categoryView.getCategory3Id());
            goods.setCategory1Name(categoryView.getCategory1Name());
            goods.setCategory2Name(categoryView.getCategory2Name());
            goods.setCategory3Name(categoryView.getCategory3Name());
        }

        // 获取平台属性集合
//        List<BaseAttrInfo> attrList = productFeignClient.getAttrList(skuId);
//        if (attrList != null){
//            List<SearchAttr> searchAttrList = new ArrayList<>();
//            for (BaseAttrInfo baseAttrInfo : attrList) {
//                SearchAttr searchAttr = new SearchAttr();
//                searchAttr.setAttrId(baseAttrInfo.getId());
//                searchAttr.setAttrName(baseAttrInfo.getAttrName());
//                searchAttr.setAttrValue(baseAttrInfo.getAttrValueList().get(0).getValueName());
//                searchAttrList.add(searchAttr);
//            }
//            goods.setAttrs(searchAttrList);
//        }
        List<BaseAttrInfo> attrList = this.productFeignClient.getAttrList(skuId);
        List<SearchAttr> searchAttrList = new ArrayList<>();
        for (BaseAttrInfo baseAttrInfo : attrList) {
            SearchAttr searchAttr = new SearchAttr();
            //  赋值平台属性Id
            searchAttr.setAttrId(baseAttrInfo.getId());
            //  赋值平台属性名
            searchAttr.setAttrName(baseAttrInfo.getAttrName());
            //  赋值平台属性值名 只有一个
            searchAttr.setAttrValue(baseAttrInfo.getAttrValueList().get(0).getValueName());
            //  添加到集合
            searchAttrList.add(searchAttr);
        }
        //  赋值数据
        goods.setAttrs(searchAttrList);
        goodsRepository.save(goods);
    }

    /**
     * 商品的下架功能
     * @param skuId
     */
    @Override
    public void lowerGoods(Long skuId) {
        goodsRepository.deleteById(skuId);
    }

    /**
     * 热点数据排名
     * @param skuId
     */
    @Override
    public void incrHotScore(Long skuId) {
        // 定义key
        String hotKey = "hotScore";

        Double aDouble = redisTemplate.opsForZSet().incrementScore(hotKey, "skuId:" + skuId, 1);

        // 如果redis中的热点key大于10，修改es
        if (aDouble % 10 == 0){

            Optional<Goods> goods = goodsRepository.findById(skuId);

            Goods goods1 = goods.get();

            goods1.setHotScore(Math.round(aDouble));

            goodsRepository.save(goods1);

        }


    }

    /**
     * es 查询接口
     * @param searchParam
     * @return
     */
    @Override
    public SearchResponseVo search(SearchParam searchParam) {
        /**
         *     private List<SearchResponseTmVo> trademarkList;
         *     private List<SearchResponseAttrVo> attrsList = new ArrayList<>();
         *     private List<Goods> goodsList = new ArrayList<>();
         *     private Long total;//总记录数
         *
         *     ---------------------------------------------
         *     先设置他们三个的属性
         *     private Integer pageSize;//每页显示的内容
         *     private Integer pageNo;//当前页面
         *     private Long totalPages;
         */
        if (searchParam != null){

            // 设置一个方法，来判断用户是根据哪个方式进的检索
            // 可能是关键词，也可能是分类id
            SearchRequest searchRequest = this.getSerachRequest(searchParam);

            // 调用高级客户端，来执行语句
            SearchResponse searchResponse = null;
            try {
                searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
            } catch (IOException e) {
                e.printStackTrace();
            }

            //------------------------------------
            // 设置一个方法，将返回值设置到实体类中
            SearchResponseVo searchResponseVo = this.searchVo(searchResponse);
            searchResponseVo.setPageSize(searchParam.getPageSize());
            searchResponseVo.setPageNo(searchParam.getPageNo());
            // 设置一共有多少页的时候要注意
            searchResponseVo.setTotalPages((searchResponseVo.getTotal() + searchParam.getPageSize() -1)/searchParam.getPageSize());

            return searchResponseVo;
        }
        return null;
    }

    private SearchResponseVo searchVo(SearchResponse searchResponse) {
        /**
         * private List<SearchResponseTmVo> trademarkList;
         * private List<SearchResponseAttrVo> attrsList = new ArrayList<>();
         * private List<Goods> goodsList = new ArrayList<>();
         * private Long total;//总记录数
         */
        SearchResponseVo searchResponseVo = new SearchResponseVo();
        // 封装数据
        SearchHits hits = searchResponse.getHits();
        TotalHits totalHits = hits.getTotalHits();
        // 获取到总条数，将总条数放入到返回对象中
        long value = totalHits.value;
        searchResponseVo.setTotal(value);

        // private List<Goods> goodsList = new ArrayList<>();
        List<Goods> goodsList = new ArrayList<>();
        // 获取goods数据
        SearchHit[] hits1 = searchResponse.getHits().getHits();
        if (hits1 != null && hits1.length>0){
            for (SearchHit hit : hits1) {
                // 获取到具体的数据
                String sourceAsString = hit.getSourceAsString();
                // 将string转换成Java对象
                Goods goods = JSON.parseObject(sourceAsString, Goods.class);
                // 判断，如果有高亮显示的话，将title设置为高亮
                if (hit.getHighlightFields().get("title") != null){
                    Text title = hit.getHighlightFields().get("title").getFragments()[0];
                    goods.setTitle(title.toString());
                }
                // 添加到list集合
                goodsList.add(goods);
            }
        }
        // 赋值
        searchResponseVo.setGoodsList(goodsList);

         // private List<SearchResponseTmVo> trademarkList;
        // 获取数据
        Map<String, Aggregation> stringAggregationMap = searchResponse.getAggregations().asMap();
        // 获取指定的数据
        ParsedLongTerms tmIdAgg = (ParsedLongTerms) stringAggregationMap.get("tmIdAgg");
        List<SearchResponseTmVo> trademarkList = tmIdAgg.getBuckets().stream().map((bucket) -> {
            SearchResponseTmVo searchResponseTmVo = new SearchResponseTmVo();
            // 获取品牌id
            String keyAsString = ((Terms.Bucket) bucket).getKeyAsString();
            searchResponseTmVo.setTmId(Long.parseLong(keyAsString));
            // 获取品牌名称
            Map<String, Aggregation> asMap = ((Terms.Bucket) bucket).getAggregations().getAsMap();
            ParsedStringTerms tmNameAgg = (ParsedStringTerms) asMap.get("tmNameAgg");
            String tmName = tmNameAgg.getBuckets().get(0).getKeyAsString();
            // 赋值
            searchResponseTmVo.setTmName(tmName);

            // 获取品牌img
            Map<String, Aggregation> tmLogoUrlMap = ((Terms.Bucket) bucket).getAggregations().getAsMap();
            ParsedStringTerms tmLogoUrlAgg = (ParsedStringTerms) tmLogoUrlMap.get("tmLogoUrlAgg");
            String tmLogoUrl = tmLogoUrlAgg.getBuckets().get(0).getKeyAsString();
            // 赋值
            searchResponseTmVo.setTmLogoUrl(tmLogoUrl);

            return searchResponseTmVo;
        }).collect(Collectors.toList());


        // 赋值
        searchResponseVo.setTrademarkList(trademarkList);

         // private List<SearchResponseAttrVo> attrsList = new ArrayList<>();
        ParsedNested attrAgg = (ParsedNested) stringAggregationMap.get("attrAgg");
        ParsedLongTerms attrIdAgg = attrAgg.getAggregations().get("attrIdAgg");

        List<SearchResponseAttrVo> searchResponseAttrVos = attrIdAgg.getBuckets().stream().map((bucket) -> {
            SearchResponseAttrVo searchResponseAttrVo = new SearchResponseAttrVo();
            // 获取平台属性id
            Number keyAsNumber = ((Terms.Bucket) bucket).getKeyAsNumber();
            searchResponseAttrVo.setAttrId(keyAsNumber.longValue());

            // 获取平台属性名
            ParsedStringTerms attrNameAgg = ((Terms.Bucket) bucket).getAggregations().get("attrNameAgg");
            String keyAsString = attrNameAgg.getBuckets().get(0).getKeyAsString();
            searchResponseAttrVo.setAttrName(keyAsString);

            // 获取平台属性值
          //  ParsedStringTerms attrValueAgg = ((Terms.Bucket) bucket).getAggregations().get("attrValueAgg");
          //  List<String> collect = attrValueAgg.getBuckets().stream().map(Terms.Bucket::getKeyAsString).collect(Collectors.toList());
           // searchResponseAttrVo.setAttrValueList(collect);

            ParsedStringTerms attrValueAgg = ((Terms.Bucket) bucket).getAggregations().get("attrValueAgg");
            List<String> valueNameList = attrValueAgg.getBuckets().stream().map((bucket1)->{
                return ((Terms.Bucket) bucket1).getKeyAsString();
            }).collect(Collectors.toList());
            searchResponseAttrVo.setAttrValueList(valueNameList);

            return searchResponseAttrVo;
        }).collect(Collectors.toList());

        // 赋值
        searchResponseVo.setAttrsList(searchResponseAttrVos);

        // 返回数据
        return searchResponseVo;
    }

    /**
     * 动态生成dsl语句，但会searchrequest对象
     * @param searchParam
     * @return
     */
    private SearchRequest getSerachRequest(SearchParam searchParam) {

        // 创建一个执行构造器
        SearchSourceBuilder builder = new SearchSourceBuilder();
        // 创建queryBulid {query -- bool}
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();

        // 判断 是否根据关键字进行检索
        if (!StringUtils.isEmpty(searchParam.getKeyword())){
            // 如果不为空，则表示是根据关键词进入的检索
            // {query -- bool -- must -- match -- title -- value}
            boolQueryBuilder.must(QueryBuilders.matchQuery("title",searchParam.getKeyword()).operator(Operator.AND));
        }

        // 判断是否是从分类id进入的检索
        if (!StringUtils.isEmpty(searchParam.getCategory3Id())){
            boolQueryBuilder.filter(QueryBuilders.termQuery("category3Id",searchParam.getCategory3Id()));
        }
        if (!StringUtils.isEmpty(searchParam.getCategory2Id())){
            boolQueryBuilder.filter(QueryBuilders.termQuery("category2Id",searchParam.getCategory2Id()));
        }
        if (!StringUtils.isEmpty(searchParam.getCategory1Id())){
            boolQueryBuilder.filter(QueryBuilders.termQuery("category1Id",searchParam.getCategory1Id()));
        }

        // 判断按照品牌id进行检索 //传递过来的数据是1:小米
        String trademark = searchParam.getTrademark();
        if (!StringUtils.isEmpty(trademark)){
            // 使用分隔符将数据分割
            String[] split = trademark.split(":");
            // 判断
            if (split != null && split.length == 2){
                boolQueryBuilder.filter(QueryBuilders.termQuery("tmId",split[0]));
            }
        }

        // 按照平台属性值进行过滤
        // 平台属性可以选择多个，所以实体类中存储的是数组
        String[] props = searchParam.getProps();
        // 判断
        if (props != null && props.length > 0){
            // 循环遍历数组
            for (String prop : props) {
                // 字符串分割  24:4G:运行内存
                String[] split = prop.split(":");
                if (split.length == 3 && split != null){
                    // 组合dsl语句
                    // 创建中间的bool
                    BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
                    // 创建最里面的bool
                    BoolQueryBuilder subBoolQuery = QueryBuilders.boolQuery();

                    // 组合语句
                    subBoolQuery.must(QueryBuilders.termQuery("attrs.attrId",split[0]));
                    subBoolQuery.must(QueryBuilders.termQuery("sttrs.attrValue",split[1]));

                    // 将最里层的bool、放到中间bool中
                    boolQuery.must(QueryBuilders.nestedQuery("attrs",subBoolQuery, ScoreMode.None));

                    // 将中间层bool放到最外层中
                    boolQueryBuilder.filter(boolQuery);
                }
            }
        }

        //{query}
        SearchSourceBuilder query = builder.query(boolQueryBuilder);
        //分页
        query.from((searchParam.getPageNo()-1)*searchParam.getPageSize());
        query.size(searchParam.getPageSize());

        // 高亮显示
        HighlightBuilder highlightBuilder = new HighlightBuilder();
        highlightBuilder.field("title");
        highlightBuilder.preTags("<span style=color:red>");
        highlightBuilder.postTags("</span>");
        query.highlighter(highlightBuilder);

        // 排序 // 传过来的数据是 1：desc 1：asc
        String felid = "";
        // 判断是否排序
        String order = searchParam.getOrder();
        if (!StringUtils.isEmpty(order)){
            // 分割
            String[] split = order.split(":");
            if (split != null && split.length ==2){

                switch (split[0]){
                    case "1":
                        felid = "hotScore";
                        break;
                    case "2":
                        felid = "price";
                        break;
                }
                query.sort(felid,"asc".equals(split[1])? SortOrder.ASC : SortOrder.DESC);
            }else{
                query.sort("hotScore",SortOrder.DESC);
            }
        }

        // 品牌信息聚合
        query.aggregation(AggregationBuilders.terms("tmIdAgg").field("tmId")
                .subAggregation(AggregationBuilders.terms("tmNameAgg").field("tmName"))
                .subAggregation(AggregationBuilders.terms("tmLogoUrlAgg").field("tmLogoUrl"))
        );

        // 平台属性集合 这里稍微有点不一样 ，因为平台属性的类型是netied
        query.aggregation(AggregationBuilders.nested("attrAgg","attrs")
            .subAggregation(AggregationBuilders.terms("attrIdAgg").field("attrs.attrId")
            .subAggregation(AggregationBuilders.terms("attrNameAgg").field("attrs.attrName"))
            .subAggregation(AggregationBuilders.terms("attrValueAgg").field("attrs.attrValue"))
            )
        );

        System.out.println(query.toString());
        // 创建request对象，并指定索引库
        SearchRequest searchRequest = new SearchRequest("goods");
        // 将查询语句放入到对象中一并返回
        searchRequest.source(query);
        // 返回结果
        return searchRequest;
    }
}
